Py.Cafe

banana0000/

Hamilton Project

Hamilton Project spending dashboard

DocsPricing
  • assets/
  • .emptyFolderPlaceholder
  • Hamilton.csv
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from dash import Dash, dcc, html, Input, Output
import dash_ag_grid as dag
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import pandas as pd
import numpy as np

df = pd.read_csv("Hamilton.csv")

# Remove any duplicate column 
df = df.loc[:, ~df.columns.duplicated()]

# Convert the 'Date' column to datetime format 
df['Date'] = pd.to_datetime(df['Date']).dt.date  # Keep only date (YYYY-MM-DD)

# Extract year, month, week, and day from the 'Date' column for filtering
df['Year'] = pd.to_datetime(df['Date']).dt.year
df['Month'] = pd.to_datetime(df['Date']).dt.strftime('%b') 
df['Week'] = "Week " + pd.to_datetime(df['Date']).dt.isocalendar().week.astype(str)
df['Day'] = pd.to_datetime(df['Date']).dt.day

# Convert numeric spending values
for col in ['Daily', 'Weekly', '7-day moving average']:
    df[col] = pd.to_numeric(df[col], errors='coerce')

# Filter the dataset to include only the top 10 spending categories 
top_categories = (
    df[df['transaction_catg_renamed'] != "Total"]
    .groupby('transaction_catg_renamed')['Weekly'].sum()
    .nlargest(10)
    .index
)
df = df[df['transaction_catg_renamed'].isin(top_categories)]

# months appear in correct order
month_order = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", 
               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

# Create dropdown options with correct order
month_options = [{"label": m, "value": m} for m in month_order if m in df['Month'].unique()]
month_options.append({"label": "All", "value": "All"})

# Initialize the Dash app
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])


app.layout = dbc.Container([
    dbc.Row([  
        dbc.Col(html.H1("Hamilton Project Spending Dashboard", className="text-center"), width=12)
    ], className="mb-4"),

    dbc.Row([
        dbc.Col(dcc.Dropdown(
            id="year-dropdown",
            options=[{"label": str(y), "value": y} for y in sorted(df['Year'].unique())] +
                    [{"label": "All", "value": "All"}],
            value="All",
            clearable=False,
            style={"width": "100%"}
        ), width=3),

        dbc.Col(dcc.Dropdown(
            id="month-dropdown",
            options=month_options, 
            value="All",
            clearable=False,
            style={"width": "100%"}
        ), width=3),

        dbc.Col(dcc.Dropdown(
            id="metric-dropdown",
            options=[
                {"label": "Daily", "value": "Daily"},
                {"label": "Weekly", "value": "Weekly"},
                {"label": "7-Day Moving Average", "value": "7-day moving average"},
            ],
            value="Weekly",
            clearable=False,
            style={"width": "100%"}
        ), width=3),
    ], className="mb-4"),

    # Radio button
    dbc.Row([
        dbc.Col(dcc.RadioItems(
            id="chart-type",
            options=[
                {"label": "Bar Chart", "value": "bar"},
                {"label": "Line Chart", "value": "line"}
            ],
            value="bar",  
            inline=True,
            labelStyle={"margin-right": "10px"}
        ), width=12)
    ], className="mb-4"),

    # Chart
    dbc.Row([  
        dbc.Col(dcc.Graph(id="chart"), width=12),
    ], className="mb-4"),

    # Data table
    dbc.Row([  
        dbc.Col(
            dag.AgGrid(
                id="data-table",
                rowData=df.to_dict("records"),
                columnDefs=[
                    {"field": "Date", 'filter': True, 'sortable': True},
                    {"field": "Year", 'filter': True, 'sortable': True},
                    {"field": "Month", 'filter': True, 'sortable': True},
                    {"field": "Week", 'filter': True, 'sortable': True},
                    {"headerName": "Transaction category", "field": "transaction_catg_renamed", 'filter': True, 'sortable': True},
                    {"field": "Daily", 'filter': True, 'sortable': True},
                    {"field": "Weekly", 'filter': True, 'sortable': True},
                    {"field": "7-day moving average", 'filter': True, 'sortable': True}
                ],
                dashGridOptions={"pagination": True, "domLayout": "autoHeight"},
                columnSize="sizeToFit",
                style={"width": "100%"}
            ),
            width=12
        )
    ])
], fluid=True, style={"backgroundColor": "#f4f4f4", "padding": "30px"})

# -------------------------
# Callbacks for Interactivity
# -------------------------

@app.callback(
    [Output("chart", "figure"), Output("data-table", "rowData")],
    [Input("year-dropdown", "value"), 
     Input("month-dropdown", "value"), 
     Input("metric-dropdown", "value"),
     Input("chart-type", "value")] 
)
def update_content(selected_year, selected_month, selected_metric, chart_type):
    filtered_df = df.copy()

    if selected_year != "All":
        filtered_df = filtered_df[filtered_df['Year'] == selected_year]

    if selected_month != "All":
        filtered_df = filtered_df[filtered_df['Month'] == selected_month]

    # Set x and y axis data based on metric selection
    if selected_metric == "Weekly":
        x_axis_data, y_axis_data = filtered_df['Week'], filtered_df['Weekly']
    elif selected_metric == "Daily":
        x_axis_data, y_axis_data = filtered_df['Day'], filtered_df['Daily']
    else:
        x_axis_data, y_axis_data = filtered_df['Date'], filtered_df['7-day moving average']

   
    fig = go.Figure()

    # Loop through categories and add traces based on selected chart type
    for category in filtered_df['transaction_catg_renamed'].unique():
        category_data = filtered_df[filtered_df['transaction_catg_renamed'] == category]
        
        if chart_type == "bar":
            fig.add_trace(go.Bar(
                x=category_data[x_axis_data.name], 
                y=category_data[y_axis_data.name], 
                name=category
            ))
        else:  # Line Chart
            fig.add_trace(go.Scatter(
                x=category_data[x_axis_data.name], 
                y=category_data[y_axis_data.name], 
                mode="lines",
                name=category
            ))

    # Update layout
    fig.update_layout(
        title=f"{selected_metric} Spending Over Time",
        xaxis_title="Time",
        yaxis_title="Spending in Billions USD",
        plot_bgcolor='white',
        barmode='stack' if chart_type == "bar" else None,  
        dragmode="drawopenpath", #default setting
        #dragmode="zoom",
        modebar_add=["drawopenpath", "drawclosedpath", "eraseshape"]
    )

    return fig, filtered_df.to_dict("records")


if __name__ == "__main__":
    app.run(debug=True)